package org.jpropelleralt.jview.ref.impl;

import java.util.Map;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.border.EmptyBorder;

import org.jpropelleralt.box.Box;
import org.jpropelleralt.change.Change;
import org.jpropelleralt.jview.JView;
import org.jpropelleralt.jview.general.GeneralRenderer;
import org.jpropelleralt.jview.general.impl.GeneralRendererDefault;
import org.jpropelleralt.jview.general.impl.GeneralRendererDefault.RenderFormat;
import org.jpropelleralt.ref.Ref;
import org.jpropelleralt.view.View;
import org.jpropelleralt.view.Views;
import org.jpropelleralt.view.update.UpdateManager;

/**
 * A non-editing {@link View} delegating to a {@link GeneralRenderer}
 * to render values, and displaying as a {@link JLabel}
 */
public class GeneralView implements JView {

	private final Ref<?> value;
	
	private final UpdateManager updateManager;
	private final GeneralRenderer renderer;
	private final JLabel label;

	private GeneralView(Ref<?> value, GeneralRenderer renderer, UpdateManager updateManager) {
		super();
		this.value = value;
		this.renderer = renderer;
		
		this.updateManager = updateManager;
		updateManager.registerUpdatable(this);

		this.label = new LinkingJLabel(this);
		label.setBorder(new EmptyBorder(0,3,0,3));
		
		value.features().addView(this);
		
		update();
	}

	/**
	 * Get the {@link JComponent} used for display
	 * @return	The {@link JLabel}
	 */
	public JLabel getComponent() {
		return label;
	}
	
	@Override
	public void changes(Map<Box, Change> changes) {
		updateManager.updateRequiredBy(this);
	}
	
	@Override
	public void update() {
		Object o = value.get();
		
		if (o == null) {
			label.setIcon(null);
			label.setText("");
			return;
		}
		
		if (renderer.canRender(o)){
			label.setIcon(renderer.getIcon(o));
			label.setText("<html>" + renderer.getHTML(o) + "</html>");
		} else {
			label.setIcon(null);
			label.setText(o.toString());			
		}
	}

	@Override
	public boolean selfNaming() {
		//Just displays value
		return false;
	}
	
	@Override
	public Format format() {
		return Format.SINGLE_LINE;
	}
	
	/**
	 * Create a {@link GeneralView}, single line
	 * @param value		The value to display
	 * @return			A new {@link GeneralView}
	 */
	public static GeneralView create(Ref<?> value) {
		return new GeneralView(value, new GeneralRendererDefault(RenderFormat.SINGLE_LINE), Views.updateManager());
	}

	/**
	 * Create a {@link GeneralView}
	 * @param value			The value to display
	 * @param renderer 		The {@link GeneralRenderer} to use
	 * @return				A new {@link GeneralView}
	 */
	public static GeneralView create(Ref<?> value, GeneralRenderer renderer) {
		return new GeneralView(value, renderer, Views.updateManager());
	}
	
	/**
	 * Create a {@link GeneralView}
	 * @param value			The value to display
	 * @param renderer 		The {@link GeneralRenderer} to use
	 * @param updateManager	The {@link UpdateManager} to use
	 * @return				A new {@link GeneralView}
	 */
	public static GeneralView create(Ref<?> value, GeneralRenderer renderer, UpdateManager updateManager) {
		return new GeneralView(value, renderer, updateManager);
	}

	/**
	 * This is the same as the parent {@link JComponent}, 
	 * the only difference is that it remembers its {@link JView}, 
	 * so that as long as the {@link JComponent} is in
	 * use, the {@link JView} survives as well. Otherwise, the 
	 * {@link JView} can be GCed while the {@link JComponent} is still 
	 * in use, stopping the {@link JComponent} being updated.
	 * Otherwise, it is very easy for the {@link JView} to be collected,
	 * since a {@link View} is NOT kept alive by the data it is
	 * watching, and is generally not of much interest after its
	 * component has been added to a UI. 
	 */
	public static class LinkingJLabel extends JLabel {
		private final JView owner;
		private LinkingJLabel(JView owner) {
			super();
			this.owner = owner;
		}
		/**
		 * Get the owner {@link JView}
		 * @return	The owner of this component
		 */
		public JView owner() {
			return owner;
		}
	}
	
}
